﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Reflection;
using System.Xml;
using System.Collections;
using System.IO;

namespace Skynet.CommonLibrary.Extension.XML
{
    /// <summary>
    /// XML 到实体的转换
    /// </summary>
    /// <remarks>
    /// 作者：         李志强
    /// 开发时间：  2011-04-11
    /// </remarks>
    public static class XMLExtension
    {
        /// <summary>
        /// 根据 xmlattribute 转换成单个实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="attribute"></param>
        /// <param name="entity"></param>
        public static void ToEntity<T>(this IEnumerable<XAttribute> attribute, T entity) where T : class
        {
            if (attribute == null || entity == null)
                return;
            Type itemType = typeof(T);
            PropertyInfo[] infos = itemType.GetProperties();
            foreach (XAttribute a in attribute)
            {
                if (infos.FirstOrDefault(o => System.String.Compare(o.Name, a.Name.LocalName, System.StringComparison.OrdinalIgnoreCase) == 0) != null)
                {
                    entity.SetValue(a.Name.LocalName, a.Value);
                }
            }
        }

        /// <summary>
        /// 通过 xmlelement 转换成 单个实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="element"></param>
        /// <returns></returns>
        public static T ToEntity<T>(this  IEnumerable<XElement> element) where T : class
        {
            if (element == null)
                return null;
            T entity = (T)typeof(T).CreateInstance();  // Activator.CreateInstance(typeof(T));
            Type itemType = typeof(T);
            PropertyInfo[] infos = itemType.GetProperties();
            foreach (XElement a in element)
            {
                if (infos.FirstOrDefault(o => System.String.Compare(o.Name, a.Name.LocalName, System.StringComparison.OrdinalIgnoreCase) == 0) != null)
                {
                    entity.SetValue(a.Name.LocalName, a.Value);
                }
            }
            return entity;
        }

        /// <summary>
        /// 通过 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="nodeList"></param>
        /// <returns></returns>
        public static T ToEntity<T>(this XmlNodeList nodeList) where T : class
        {
            if (nodeList == null)
                return null;
            T entity = (T)typeof(T).CreateInstance();  // Activator.CreateInstance(typeof(T));
            Type itemType = typeof(T);

            foreach (XmlNode a in nodeList)
            {
                if (itemType.GetIgnPropertyInfo(a.Name) != null)
                {
                    if (a.ChildNodes.Count > 0)
                    {
                        entity.SetValue(a.Name, a.ChildNodes[0].Value);
                    }
                }
            }
            return entity;
        }

        #region EntityToXML

        /// <summary>
        /// 将元素转换成XML
        /// </summary>
        public static string EntityToXML<T>(this T entity) where T : class
        {
            Type t = typeof(T);

            string entityName = t.Name.Split(new string[] { "`" }, StringSplitOptions.RemoveEmptyEntries)[0];
            XDocument document = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes"),
                new XElement(entityName));


            //创建行
            //实例化载体
            if (t.GetInterface(typeof(IList).FullName) != null)
            {
                CreateListElement((IList)entity, document.Root, "");
            }
            else
            {
                CreateRowElement(t, entity, document.Root);
            }

            string xml = string.Format("{0}{1}{2}",
                document.Declaration,
                Environment.NewLine,
                document);
            return xml;
        }
        /// <summary>
        /// 通过固定格式的实体转换成XML文件
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="list">集合</param>
        /// <param name="commons">添加注释信息 委托</param>
        /// <param name="progress">进度委托</param>
        /// <returns>XML</returns>
        public static string EntityToXML<T>(List<T> list, Func<XComment[]> commons, Action<int, int> progress) where T : class
        {
            Type t = typeof(T);
           

            string entityName = t.Name.Split(new string[] { "`" }, StringSplitOptions.RemoveEmptyEntries)[0];
            XDocument document = new XDocument(
                new XDeclaration("1.0", "utf-8", "yes"),
                new XElement(entityName));
            //添加注释信息
            if (commons != null)
            {
                foreach (XComment xc in commons())
                {
                    if (document.Root != null) 
                        document.Root.Add(xc);
                }
            }
            //创建行
            CreateListElement(list, document.Root, "");

            return string.Format("{0}{1}{2}",
                document.Declaration,
                Environment.NewLine,
                document);
        }
        public static string EntityToXML<T>(List<T> list, Func<XComment[]> commons) where T : class
        {
            return EntityToXML(list, commons, null);
        }
        /// <summary>
        /// 通过实体转换成XML字符串
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="list">集合</param>
        /// <returns>xml字符串</returns>
        public static string EntityToXML<T>(List<T> list) where T : class
        {
            return EntityToXML(list, null);
        }

        private static void CreateRowElement(Type t, object entity, XElement element)
        {
            //查看序号
            PropertyInfo[] pinfos = t.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
            Dictionary<int, PropertyInfo> dicinfo = new Dictionary<int, PropertyInfo>();
            foreach (var p in pinfos)
            {
                if (p.GetCustomAttributes(typeof(XMLTypeAttribute), false).Any())
                {
                    XMLTypeAttribute xmlType = p.GetCustomAttributes(typeof(XMLTypeAttribute), false)[0] as XMLTypeAttribute;
                    if (xmlType != null) 
                        dicinfo[xmlType.Index] = p;
                }
            }
            //循环所有属性(除父类)
            foreach (PropertyInfo p in pinfos)
            {
                Type pType = p.GetPropertyType();
                //集合
                if (pType.GetInterface(typeof(IList).FullName) != null)
                {
                    IList obj = (IList)p.GetValue(entity, null);
                    if (obj != null && obj.Count > 0)
                    {
                        //创建列表
                        CreateListElement(obj, element, p.Name);
                    }
                }
                    //普通值
                else if (pType.ToString() == "System.String" || pType.IsValueType)
                {
                    object value = p.GetValue(entity, null);
                    AddElement(pType, element, p, value);
                }
                    //单个对象
                else
                {
                    XElement firstEntity = new XElement(p.Name);
                    object itemObj = p.GetValue(entity, null);
                    if (itemObj != null)
                    {
                        CreateRowElement(pType, itemObj, firstEntity);
                        element.Add(firstEntity);
                    }
                }
            }
        }
        private static void AddElement(Type pType, XElement element, PropertyInfo info, object value)
        {
            bool isAttribute = false;
            string dateFormat = "yyyy-MM-ddTHH:mm:ss";
            if (info.IsDefined(typeof(XMLTypeAttribute), false))
            {
                XMLTypeAttribute xmlType = info.GetCustomAttributes(typeof(XMLTypeAttribute), false)[0] as XMLTypeAttribute;
                if (xmlType != null)
                {
                    isAttribute = xmlType.IsAttribute;
                    dateFormat = xmlType.DateFormat;
                }
            }
            if (pType.ToString() == "System.DateTime")
            {
                if (isAttribute)
                {
                    element.Add(new XAttribute(info.Name, value != null ? ((DateTime)value).ToString(dateFormat) : ""));
                }
                else
                {
                    element.Add(new XElement(info.Name, value != null ? ((DateTime)value).ToString(dateFormat) : ""));
                }
            }
            else
            {
                if (isAttribute)
                {
                    element.Add(new XAttribute(info.Name, value ?? ""));
                }
                else
                {
                    element.Add(new XElement(info.Name, value ?? ""));
                }
            }
        }
        private static void CreateListElement(IList list, XElement element, string rowname)
        {
            foreach (var o in list)
            {
                XElement row = new XElement(rowname);
                CreateRowElement(o.GetType(), o, row);
                element.Add(row);
            }

        }
        #endregion

        #region XMLToEntity
        /// <summary>
        /// 将XML转换成实体
        /// </summary>
        /// <param name="xmlstring">XML </param>
        /// <returns>实体</returns>
        internal static T XMLToEntity<T>(this string xmlstring) where T : class
        {

            XDocument document = XDocument.Load(new StringReader(xmlstring));
            Type type = typeof(T);
            T entity = type.CreateInstance<T>();    // Activator.CreateInstance(type);

            ForeachElement(document.Elements(), typeof(T), entity);

            return entity;
        }
        private static void ForeachElement(IEnumerable<XElement> element, Type type, object entity)
        {
            foreach (XElement root in element)
            {
                //检查属性
                foreach (XAttribute xa in root.Attributes())
                {
                    entity.SetValue(xa.Name.LocalName, xa.Value);
                }
                //检查子元素
                #region
                if (root.Elements().Any())
                {
                    string name = root.Elements().First().Name.LocalName;
                    PropertyInfo info = type.GetIgnPropertyInfo(name);
                    if (info != null)
                    {
                        Type pType = info.GetPropertyType();
                        //如何是集合
                        #region 集合
                        if (pType.GetInterface(typeof(IList).FullName) != null)
                        {
                            //实例化 子类型
                            Type itemType = pType.GetGenericArguments()[0];

                            //反射创建泛型集合
                            Type generic = typeof(List<>);
                            Type[] typeArgs1 = { itemType };
                            generic = generic.MakeGenericType(typeArgs1);
                            var list = generic.CreateInstance<IList>();
                            //处理
                            foreach (XElement item in root.Elements())
                            {
                                object itemEntity; 
                                if (item.Elements().Any())
                                {
                                    //如果有元素而没有属性 表示是列表而且有多个
                                    if (!item.Attributes().Any())
                                    {

                                        itemEntity = itemType.CreateInstance();
                                        ForeachElement(item.Elements(), itemType, itemEntity);
                                        //加入集合
                                        list.Add(itemEntity);
                                        continue;
                                    }
                                }
                                itemEntity = itemType.CreateInstance();
                                ForeachElement(new XElement[] { item }, itemType, itemEntity);
                                //加入集合
                                list.Add(itemEntity);
                            }
                            //赋值
                            entity.SetValue(name, list);
                        }
                        #endregion
                        //单个对象
                        #region 对象
                        else if (pType.BaseType != null && pType.BaseType.ToString() == "System.Object")
                        {
                            //实例化 子类型
                            Type itemType = info.PropertyType;
                            object itemEntity = itemType.CreateInstance();
                            //递归处理
                            ForeachElement(root.Elements(), itemType, itemEntity);
                            //赋值
                            entity.SetValue(name, itemEntity);
                        }
                        #endregion
                    }
                }
                #endregion

                entity.SetValue(root.Name.LocalName, root.Value);
            }
        }
        #endregion

    }
}
